﻿/*
	默认作用的输入法有：多多、小小、小狼毫、小科狗、微软五笔、搜狗拼音、搜狗五笔、手心，其它输入法需要自己在托盘菜单点击「获取Class」按提示获取后打开helper\Setting.ini以|分隔追加至WinClass中立即生效！
*/

ToolVersion:="3.0.1.0"
;@Ahk2Exe-Let U_version = %A_PriorLine~U)^(.+"){1}(.+)".*$~$2%	;读取版本号以编译
;@Ahk2Exe-SetMainIcon Compiler\tools.ico  ; 指定托盘图标文件
;@Ahk2Exe-AddResource Compiler\tools.ico, 160  ; 替换自带的'蓝色H'图标
;@Ahk2Exe-AddResource Compiler\lock.ico, 206  ; 替换自带的'绿色S'图标
;@Ahk2Exe-AddResource Compiler\tools.ico, 207  ; 替换自带的'红色H'图标
;@Ahk2Exe-AddResource Compiler\lock.ico, 208  ; 替换自带的'红色S'图标
;@Ahk2Exe-Bin %A_ScriptDir%\Compiler\Unicode 32-bit.bin, %A_Desktop%\候选框截图工具.exe, CP65001  ; 编译的bin文件路径
;@Ahk2Exe-ExeName %A_Desktop%\候选框截图工具.exe  ; 打包后的exe文件路径
;@Ahk2Exe-SetCompanyName 98五笔资源库  ; 企业信息
;@Ahk2Exe-SetCopyright 98五笔资源库  ; 版权信息
;@Ahk2Exe-SetDescription 输入法候选框截图工具  ; 文件说明
;@Ahk2Exe-SetFileVersion %U_version%  ; 文件版本
;@Ahk2Exe-SetInternalName 输入法候选框截图工具  ; 文件内部名
;@Ahk2Exe-SetLanguage 0x0804  ; 区域语言
;@Ahk2Exe-SetName 输入法候选框截图工具  ; 名称
;@Ahk2Exe-SetProductName 输入法候选框截图工具  ; 产品名称
;@Ahk2Exe-SetProductVersion %U_version%  ; 产品版本号
;@Ahk2Exe-SetVersion %U_version%  ; 版本号

#NoEnv
#MaxMem 2048
;#NoTrayIcon
#SingleInstance, Force
SetBatchLines, -1
#MaxThreadsPerHotkey 100
#MaxHotkeysPerInterval 400
#Persistent
#WinActivateForce
SetTitleMatchMode, RegEx
DetectHiddenWindows, Off
#Include %A_ScriptDir%
#Include <ImagePut>
#Include <Gdip_All>
#Include <Subtitle>
#Include <ScriptGuard>
#Include <Class_EasyIni>
OnMessage(0x404, "AHK_NOTIFYICON")
;文件编码为utf-8带签名，热键改为Space更劲爆！
;初始化配置
default_obj:={SetHotkey:{hotkey:"Space"}
	,SetWinClass:{WinClass:"ATL:|VNDDSKIN_|CKegServer|yong_input|SoPY_Comp|SoWB_Comp|PalmInputUICand|ICand|Iime_CandidateWindow|QQWubiCompWndII|ImeCandwinime"}
	,SetSuspendedHotkey:{SuspendedHotkey:"!a"}
	,SetGrayscale:{IsGrayscale:0}
	,SetWatchReturn:{IsWatchReturn:0
		,WatchWinClass:"TXGuiFoundation|WeChatMainWndForPC"}
	,RenderToImage:{bgcolor:"0xFFFFFFFF",ImageRadius:0,FontSize:32,ImageMargin:1
		,TextColor:"0xFF333333",FontName:"宋体",Fontbold:True,RenderHotkey:"AppsKey"
		,IsOutline:0,outline_stroke:1,outline_color:"E81010",outline_glow:"3",outline_tint:"FFB666"
		,IsCutting:True,IsVertical:True,RandomColor:0,IsAdminRun:0}}
;配置项说明
DefaultComments:={SetHotkey:{hotkey:"设置触发热键，热键改为Space更劲爆！特殊键表达方式：Shift—>+、Ctrl—>^、Alt—>!"}
		,SetWinClass:{WinClass:"设置窗口Class，多个窗口类用|分隔。"}
		,SetSuspendedHotkey:{SuspendedHotkey:"小工具禁用／恢复热键！"}
		,SetGrayscale:{IsGrayscale:"截图是否统一（黑白风格）灰度图显示，1为是0为否"}
	,SetWatchReturn:{IsWatchReturn:"是否监控QQ或微信的消息发送，自动文字转化为图片"
		,WatchWinClass:"监控消息发送窗口的Class，多个窗口类用|分隔。"}
	,RenderToImage:{bgcolor:"图片背景色，透明背景0xFF改为0x00"
		,ImageRadius:"图片圆角大小"
		,FontSize:"图片字体大小"
		,ImageMargin:"图片字体页边距"
		,TextColor:"图片字体颜色"
		,FontName:"图片字体名称"
		,Fontbold:"图片字体是否加粗0或1"
		,RenderHotkey:"文字转图片功能热键，选中文字后触发！"
		,IsOutline:"字体线条是否突出显示！"
		,outline_stroke:"字体线条像素宽度"
		,outline_color:"字体线条突出颜色"
		,outline_glow:"字体边缘发光高亮突出值"
		,outline_tint:"字体边缘发光高亮色值"
		,IsCutting:"多个文字全部纵向是否分割多图片显示！"
		,IsVertical:"多个文字全部纵向显示！"
		,RandomColor:"随机字体色"
		,IsAdminRun:"是否以管理员身份运行"}}

RandomColorList:={light:[["0xFFFFC0CB","粉红色"],["0xFFE6E6FA","薰衣草"],["0xFFF0E68C","卡其色"],["0xFFF5FFFA","薄荷奶油"],["0xFFF5F5DC","米色"],["0xFFFFB6C1","浅粉红"],["0xFFFFEBCD","白杏仁"],["0xFFFFB7C5","樱花粉"],["0xFFFFD300","黄色"],["0xFFFFDF00","金黄"],["0xFFFFFFF0","象牙"],["0xFF98FF98","薄荷绿"],["0xFFFAE6FA","淡紫色（潘通）"],["0xFFF4C430","藏红花"],["0xFFDBD7D2","森林狼"],["0xFFFFF0F5","薰衣草腮红"]]
	,deep:[["0xFFFF69B4","热粉色"],["0xFFDB7093","紫罗兰"],["0xFFFF00FF","洋红"],["0xFF800080","紫色"],["0xFFCD5C5C","印度红"],["0xFF4B0082","靛蓝"],["0xFF32CD32","酸橙绿"],["0xFF8FBC8F","深海洋绿"],["0xFF8080","水鸭"],["0xFFB8860B","黑沉香"],["0xFFDEB887","硬木色"],["0xFF841B2D","古红"],["0xFFCD9575","古铜"],["0xFFA52A2A","赤褐色的"],["0xFF87A96B","芦笋"],["0xFF54626F","黑珊瑚"]]}

if !FileExist(A_ScriptDir "\helper")
	FileCreateDir,%A_ScriptDir%\helper
CaptureIni:=class_EasyIni(IniPath:=A_ScriptDir "\helper\Setting.ini")
FileInstall,helper\颜色值参考.ini,%A_ScriptDir%\helper\颜色值参考.ini
Gosub TrayMenu
Gosub CheckCaptureIni
if IsAdminRun&&!A_IsAdmin {
	RunAsAdmin()
}
Gosub OnSetting
EmptyMem()

#if WinActive("ahk_group RenderClass")&&IsWatchReturn
	$vk0d::
		Gosub TextToImage
		Send,{vk0dsc1c}
	Return

	$^vk0d::
		Gosub TextToImage
		Send,^{vk0dsc1c}
	Return
#if

Capturescreen:
	TempClass:="",aid:=0
	if !WinActive("ahk_group RenderClass")||!IsWatchReturn {
		if DllCall("User32.dll\IsClipboardFormatAvailable","UINT",0x02){
			OpenClipboard(),EmptyClipboard(),CloseClipboard()
		}
		Gosub Screenshot_Windows
		if DllCall("User32.dll\IsClipboardFormatAvailable","UINT",0x02)&&WinExist("ahk_id " aid){
			if (Format("0x{:x}",WinExist("A"))<>aid&&aid&&TempClass){
				if WinExist("ahk_class " TempClass){
					Send, {Escape}
					WinHide,ahk_class %TempClass%
				}
				SendInput, ^{vk56sc02F}
			}Else{
				Thishotkey:= Trim(A_Thishotkey,"$")
				SendInput,{%Thishotkey%}
			}
		}else{
			Thishotkey:= Trim(A_Thishotkey,"$")
			SendInput,{%Thishotkey%}
		}
		;;防止其它脚本定义Ctrl或Shift造成干扰
		if GetKeyState("lCtrl")
			Send,{lCtrl UP}
		if GetKeyState("RCtrl")
			Send,{RCtrl UP}
		if GetKeyState("LShift")
			Send,{LShift UP}
		if GetKeyState("RShift")
			Send,{RShift UP}
	}Else{
		Thishotkey:= Trim(A_Thishotkey,"$")
		SendInput,{%Thishotkey%}
	}
return

TextToImage:
	if SelectString:=GetSelectString(IsWatchReturn){
		SelectString:=IsVertical?RegExReplace(SelectString,"m)(.)","$1`n"):SelectString
		SelectString:=Trim(SelectString,"`r`n")
		TextStyle := {}
		TextStyle.font := FontName
		TextStyle.size :=FontSize?FontSize:""
		if IsOutline {
			TextStyle.outline := {}
			TextStyle.outline.stroke :=outline_stroke?outline_stroke:0
			TextStyle.outline.color :=outline_color?RegExReplace(outline_color,"i)0x"): ""
			TextStyle.outline.glow :=outline_glow?outline_glow: 0  ;发光
			TextStyle.outline.tint :=outline_tint?RegExReplace(outline_tint,"i)0x"): ""  ;染色
		}Else{
			TextStyle.Delete("outline")
		}
		TextStyle.color := TextColor?TextColor:""
		TextStyle.margin:=ImageMargin?ImageMargin:0
		TextStyle.bold:=Fontbold?1:0

		backgroundStyle := {}
		backgroundStyle.color :=InStr(bgcolor,"0x00")?"Transparent":bgcolor
		backgroundStyle.radius:=ImageRadius?ImageRadius:""

		if IsCutting&&IsVertical {
			if DllCall("User32.dll\IsClipboardFormatAvailable","UINT",0x02){
				OpenClipboard(),EmptyClipboard(),CloseClipboard()
			}
			Loop,Parse,SelectString,`n,`r
			{
				if (RegExReplace(A_LoopField,"[\s\t\r\n]+")<>""){
					TextStyle.color:=RandomColor&&objlength(RandomColorList[grayLevel="light"?"deep":"light"])?RandomColorList[grayLevel="light"?"deep":"light",A_Index>objlength(RandomColorList[grayLevel="light"?"deep":"light"])?Mod(A_Index,objlength(RandomColorList[grayLevel="light"?"deep":"light"])):A_Index,1]:TextStyle.color
					if TexthBitmap := Subtitle.RenderToHBitmap(A_LoopField, backgroundStyle, TextStyle){
						ImagePutClipboard(IsGrayscale?HBitmapToGrayscale(TexthBitmap):TexthBitmap)
						if DllCall("User32.dll\IsClipboardFormatAvailable","UINT",0x02)
							SendInput, ^{vk56sc02F}
					}
				}
			}
		}Else{
			if TexthBitmap := Subtitle.RenderToHBitmap(SelectString, RenderStyle1, RenderStyle2){
				if DllCall("User32.dll\IsClipboardFormatAvailable","UINT",0x02){
					OpenClipboard(),EmptyClipboard(),CloseClipboard()
				}
				ImagePutClipboard(IsGrayscale?HBitmapToGrayscale(TexthBitmap):TexthBitmap)
				if DllCall("User32.dll\IsClipboardFormatAvailable","UINT",0x02)
					SendInput, ^{vk56sc02F}
			}
		}
	}Else{
		Thishotkey:= Trim(A_Thishotkey,"$")
		SendInput,{%Thishotkey%}
	}
Return

GetSelectString(SelectAll:=0){
	BlockInput,On
	lastClip:=Clipboard
	Clipboard:=""
	if SelectAll
		SendInput, ^{vk41sc01E}
	SendInput, ^{vk43sc02E}
	BlockInput,Off
	ClipWait,0.1
	if (Clipboard=="")
		Return
	string:=RegExReplace(Clipboard,"^[\r\n]+|[\r\n]+$")
	Clipboard:=lastClip
	if !ErrorLevel&&string
		Return string
}

Screenshot_Windows:
	if (aid:=WinExist("ahk_group IMECLASS"))||(aid:=WinExist("ahk_class Microsoft.IME*"))||(aid:=WinExist("ahk_class ApplicationFrameW*")) {
		WinGetClass,TempClass,ahk_id %aid%
		if TempClass {
			WinGetPos,xpos,Ypos,Width,Height,ahk_class %TempClass%
			if IsGrayscale {
				ImagePutClipboard(HBitmapToGrayscale(ImagePutHBitmap([xpos,Ypos,Width,Height])))
			}Else{
				;ImagePutWindow([xpos,Ypos,Width,Height])
				ImagePutClipboard([xpos,Ypos,Width,Height])
				;ImagePutClipboard("ahk_class " TempClass)
			}
		}
	}
Return

TrayMenu:
	Menu, TRAY, NoStandard
	Menu, TRAY, DeleteAll
	Menu, Tray, UseErrorLevel
	Menu, Tray, Color,ffffff
	Menu, TRAY,Add, 获取Class,GetWindowClass
	Menu, TRAY,Add,
	Menu, other,Add, QQ监控发送,Toggle_WatchReturn
	Menu, other,Add, 
	Menu, other,Add, 灰度图,Toggle_Grayscale
	Menu, TRAY,Add, 其它设置,:other
	Menu, TRAY,Add,
	Menu, TRAY,Add, 设置窗口,OnSetting
	Menu, TRAY,Add,
	Menu, TRAY,Add, 禁用工具,OnSuspended
	Menu, TRAY,Add,
	Menu, TRAY,Add, 重载工具,OnReload
	Menu, TRAY,Add,
	Menu, TRAY,Add, 退出工具,OnExits
	if !A_IsCompiled{
		if FileExist(A_ScriptDir "\Compiler\tools.ico")
			Menu, Tray, Icon,%A_ScriptDir%\Compiler\tools.ico,1
		Else
			Menu, Tray, Icon,shell32.dll,13
	}
	Gosub TrayMenuTip
Return

OnSetting:
	Gui,Capture:Destroy
	Gui,Capture:Default
	Gui,Capture:+HWNDCaptureGui AlwaysOnTop +LastFound -DPIScale
	Gui,Capture:Font,s10 bold
	Gui,Capture:Add,GroupBox,xm y+10 w200 vGrBox,发送设置
	GuiControlGet,grbpos,Capture:Pos,GrBox
	Gui,Capture:Font,s10 norm
	Gui,Capture:Add,Checkbox,% "x" grbposX+15 " y" grbposY+25 " gGuiControl_Gui vToggle_IsWatchReturn Checked" (IsWatchReturn?1:0),监控QQ消息逐字发送
	Gui,Capture:Add,Checkbox,% "x" grbposX+15 " gGuiControl_Gui y+15 vToggle_RandomColor Checked" (RandomColor?1:0),随机字体色
	if !IsWatchReturn
		GuiControl,Capture:Disable,Toggle_RandomColor
	Gui,Capture:Add,Checkbox,% "x+15 gGuiControl_Gui yp vToggle_IsAdminRun Checked" (IsAdminRun&&A_IsAdmin||!IsAdminRun&&A_IsAdmin?1:0),管理员身份运行
	Gui,Capture:Add,Checkbox,% "x" grbposX+15 " gGuiControl_Gui y+15 vToggle_IsGrayscale Checked" (IsGrayscale?1:0),图片灰度处理
	Gui,Capture:Add,Text,% "x" grbposX+15 " y+15 ",选择字体：
	Gui,Capture:Add,ddl,% "x+5 gGuiControl_Gui yp vSetFontName ",% GetFontslist()
	GuiControl,Capture:ChooseString,SetFontName,% FontName
	GuiControlGet,sfnpos,Capture:Pos,SetFontName
	Gui,Capture:Add,Text,% "x" grbposX+15 " y+15 ",截图热键：
	Gui,Capture:Add,Button,% "x+5 gGuiControl_Gui yp-3 vSethotkey w" sfnposW,% Format_hotkey(hotkey)
	Gui,Capture:Add,Text,% "x" grbposX+15 " y+15 ",禁用启用：
	Gui,Capture:Add,Button,% "x+5 gGuiControl_Gui yp-3 vSetSuspendedHotkey w" sfnposW,% Format_hotkey(SuspendedHotkey)
	SetButtonColor("SetHotkey", "0xF0f0f0"),SetButtonColor("SetSuspendedHotkey", "0xF0f0f0")
	Gui,Capture:Add,Button,% "x" grbposX+15 " y+15 gGuiControl_Gui vGetIMEWinClass",获取输入法窗口类
	Gui,Capture:Add,Button,% "x" grbposX+15 " y+15 gGuiControl_Gui vopenConfig",打开配置文件
	Gui,Capture:Font,s9 norm
	Gui,Capture:Add,Text,% "x+10 yp+5 cred vtip",需手动修改配置文件实时生效！
	Gui,Capture:Font,s10 norm
	GuiControlGet,ocpos,Capture:Pos,openConfig
	GuiControlGet,tippos,Capture:Pos,tip
	Gui,Capture:Font,s10 bold
	Gui,Capture:Add,GroupBox,% "h" ocposY+20 " w" tipposX+tipposW+20 " x" grbposX " y" grbposY+ocposY+45 " vGrBox2",使用简介
	GuiControlGet,grb2pos,Capture:Pos,GrBox2
	Gui,Capture:Font,s10 norm
	inifilefullpath:=CaptureIni.GetFileName()
	helpInfo=
	(join`r`n LTrim 
		1、默认作用的输入法有：多多、小小、小狼毫、（微软五笔、微软拼音需要开启兼容模式）`n  搜狗拼音、搜狗五笔、手心、冰棱等
		2、可以划选文字生成独立的文字图片到剪切版，自动粘贴！默认AppsKey「键盘上的菜单右键」。
		3、可以托盘菜单勾选「监控发送」或者自行改%inifilefullpath%`n  在指定的窗口发送信息自动转图片默认支持QQ与微信。
		4、配置文件%inifilefullpath%手动修改后保存自动生效！
		5、不在默认支持列的输入法，请手动获取输入法窗口Class，打开配置文件以|分隔追加到WinClass`n  参数后面，保存立即生效！
	)
	Gui,Capture:Font,s10 norm
	Gui,Capture:Add,Text,% "x" grbposX+15 " y" grb2posY+25 " cgray vtip2",%helpInfo%
	GuiControlGet,tip2pos,Capture:Pos,tip2
	GuiControl,Capture:Move,GrBox,% "h" ocposY+35 " w" tip2posX+tip2posW+20
	GuiControl,Capture:Move,GrBox2,% "h" tip2posH+45 " w" tip2posX+tip2posW+20
	Gui,Capture:Show,AutoSize,% A_IsAdmin?"[管理员]候选框截图设置":"候选框截图设置"
Return

GuiControl_Gui:
	Gui,Capture:Submit,NoHide
	Switch A_GuiControl
	{
		Case "Toggle_IsAdminRun":
			WatchFolder(A_ScriptDir "\helper", "**DEL")
			IsAdminRun:=CaptureIni["RenderToImage","IsAdminRun"]:=Toggle_IsAdminRun?1:0,CaptureIni.Save()
			WatchFolder(A_ScriptDir "\helper", "WatchConfDirFunc", False, 19)
			Reload
		Case "GetIMEWinClass":
			WinMinimize , ahk_id %CaptureGui%
			Gosub GetWindowClass
			WinRestore , ahk_id %CaptureGui%
		Case "Toggle_IsWatchReturn":
			Gosub Toggle_WatchReturn
			if !IsWatchReturn
				GuiControl,Capture:Disable,Toggle_RandomColor
			Else
				GuiControl,Capture:Enable,Toggle_RandomColor
		Case "Toggle_IsGrayscale":
			Gosub Toggle_Grayscale
		Case "Toggle_RandomColor":
			WatchFolder(A_ScriptDir "\helper", "**DEL")
			RandomColor:=CaptureIni["RenderToImage","RandomColor"]:=!RandomColor,CaptureIni.Save()
			WatchFolder(A_ScriptDir "\helper", "WatchConfDirFunc", False, 19)
		Case "SetFontName":
			WatchFolder(A_ScriptDir "\helper", "**DEL")
			FontName:=CaptureIni["RenderToImage","FontName"]:=%A_GuiControl%,CaptureIni.Save()
			WatchFolder(A_ScriptDir "\helper", "WatchConfDirFunc", False, 19)
		Case "SetSuspendedHotkey":
			SetButtonColor("SetSuspendedHotkey", "0xF9DA32")
			GuiControl,Capture:,%A_GuiControl%,请15秒内按下热键
			Hotkey,$%SuspendedHotkey%,OnSuspended,Off
			if ComboKey:=WaitComboKey("T15"){
				Hotkey,$%ComboKey%,OnSuspended,Useerrorlevel
				if !ErrorLevel {
					SuspendedHotkey:=CaptureIni["SetSuspendedHotkey","SuspendedHotkey"]:=ComboKey,CaptureIni.Save()
					TrayTip,候选框截图工具,热键设置成功！,,1
				}else{
					Hotkey,$%SuspendedHotkey%,OnSuspended,Useerrorlevel
					TrayTip,Error,热键设置失败！,,2
				}
				SetButtonColor("SetSuspendedHotkey", "0xF0f0f0")
			}
			SetButtonColor("SetSuspendedHotkey", "0xF0f0f0")
			GuiControl,Capture:,%A_GuiControl%,% Format_hotkey(SuspendedHotkey)
			GuiControl,Capture:,SetHotkey,% Format_hotkey(Hotkey)
		Case "SetHotkey":
			SetButtonColor("SetHotkey", "0xF9DA32")
			GuiControl,Capture:,%A_GuiControl%,请15秒内按下热键
			Hotkey,$%Hotkey%,Capturescreen,Off
			if ComboKey:=WaitComboKey("T15"){
				Hotkey,$%ComboKey%,Capturescreen,Useerrorlevel
				if !ErrorLevel {
					Hotkey:=CaptureIni["SetHotkey","Hotkey"]:=ComboKey,CaptureIni.Save()
					TrayTip,候选框截图工具,热键设置成功！,,1
				}else{
					Hotkey,$%Hotkey%,Capturescreen,Useerrorlevel
					TrayTip,Error,热键设置失败！,,2
				}
				SetButtonColor("SetHotkey", "0xF0f0f0")
			}
			SetButtonColor("SetHotkey", "0xF0f0f0")
			GuiControl,Capture:,%A_GuiControl%,% Format_hotkey(Hotkey)
			GuiControl,Capture:,SetSuspendedHotkey,% Format_hotkey(SuspendedHotkey)
		Case "openConfig":
			Gui,Capture:Destroy
			OpenAndSelect(CaptureIni.GetFileName())
			try {
				Run,%A_WinDir%\notepad.exe %inifilefullpath%
			}
	}
	Gosub TrayMenuTip
Return

WaitComboKey(Options:=""){
	InputHook := InputHook(Options)
	if !InStr(Options, "V")
		InputHook.VisibleNonText := false
	InputHook.KeyOpt("{All}", "E")  ; 结束
	; Exclude the modifiers
	InputHook.KeyOpt("{LCtrl}{RCtrl}{LAlt}{RAlt}{LShift}{RShift}{LWin}{RWin}", "-E")
	InputHook.Start()
	ErrorLevel := InputHook.Wait()  ; 将 EndReason 存储在 ErrorLevel 中
	return InputHook.EndMods . InputHook.EndKey  ; 返回字符串 如 <^<+Esc
}

;设置Button背景色
SetButtonColor(ControlID, Color,HWND_:="Capture", Margins:=2){
	GuiControlGet, hwnd, %HWND_%:Hwnd, %ControlID%
	VarSetCapacity(RECT, 16, 0), DllCall("User32.dll\GetClientRect", "Ptr", hwnd, "Ptr", &RECT)
	W := NumGet(RECT, 8, "Int") - (Margins * 1.5), H := NumGet(RECT, 12, "Int") - (Margins * 1.5)

	Color:=((Color&0xFF)<<16)+(Color&0xFF00)+((Color>>16)&0xFF)
	hbm:=CreateDIBSection(W, H), hdc := CreateCompatibleDC(), obm := SelectObject(hdc, hbm)
	hBrush:=DllCall("CreateSolidBrush", "UInt", Color, "UPtr"), obh:=SelectObject(hdc, hBrush)
	DllCall("Rectangle", "UPtr", hdc, "Int", 0, "Int", 0, "Int", W, "Int", H), SelectObject(hdc, obm)
	BUTTON_IMAGELIST_ALIGN_CENTER := 4, BS_BITMAP := 0x0080, BCM_SETIMAGELIST := 0x1602, BITSPIXEL := 0xC
	BPP := DllCall("Gdi32.dll\GetDeviceCaps", "Ptr", hdc, "Int", BITSPIXEL)
	HIL := DllCall("Comctl32.dll\ImageList_Create", "UInt", W, "UInt", H, "UInt", BPP, "Int", 6, "Int", 0, "Ptr")
	DllCall("Comctl32.dll\ImageList_Add", "Ptr", HIL, "Ptr", hbm, "Ptr", 0)
	; ; Create a BUTTON_IMAGELIST structure
	VarSetCapacity(BIL, 20 + A_PtrSize, 0), NumPut(HIL, BIL, 0, "Ptr")
	Numput(BUTTON_IMAGELIST_ALIGN_CENTER, BIL, A_PtrSize + 16, "UInt")
	SendMessage, BCM_SETIMAGELIST, 0, 0, , ahk_id %HWND%
	SendMessage, BCM_SETIMAGELIST, 0, &BIL, , ahk_id %HWND%
	SelectObject(hdc, obh), DeleteObject(hbm), DeleteObject(hBrush), DeleteDC(hdc)
}

GetFontslist(){
	static fontlist
	fontlist:=""
	If (fontlist)
		Return fontlist
	VarSetCapacity(logfont, 128, 0), NumPut(1, logfont, 23, "UChar")
	obj := []
	DllCall("EnumFontFamiliesEx", "ptr", DllCall("GetDC", "ptr", 0), "ptr", &logfont, "ptr", RegisterCallback("EnumFontProc"), "ptr", &obj, "uint", 0)
	For font in obj
		fontlist .= "|" font
	fontlist:=LTrim(fontlist,"|")
	Return fontlist
}
EnumFontProc(lpFont, tm, TextFont, lParam){
	obj := Object(lParam)
	If (TextFont>1&&!InStr(font:=StrGet(lpFont+28), "@"))
		obj[font] := 1
	Return 1
}

AHK_NOTIFYICON(wParam, lParam, uMsg, hWnd){
	if (lParam = 0x203) {
		Gosub OnSetting
	}
}

OpenAndSelect(sFullPath){
	SplitPath sFullPath, , sPath, ext, name_no_ext
	FolderPidl := DllCall("shell32\ILCreateFromPath", "Str", sPath)
	DllCall("shell32\SHParseDisplayName", "str", sFullPath, "Ptr", 0, "Ptr*", ItemPidl := 0, "Uint", 0, "Uint*", 0)
	DllCall("shell32\SHOpenFolderAndSelectItems", "Ptr", FolderPidl, "UInt", 1, "Ptr*", ItemPidl, "Int", 0)
	CoTaskMemFree(FolderPidl)
	if name_no_ext
		CoTaskMemFree(ItemPidl)
}

CoTaskMemFree(pv) {
	Return DllCall("ole32\CoTaskMemFree", "Ptr", pv)
}

Toggle_WatchReturn:
	WatchFolder(A_ScriptDir "\helper", "**DEL")
	IsWatchReturn:=CaptureIni["SetWatchReturn","IsWatchReturn"]:=!IsWatchReturn,CaptureIni.Save()
	Menu, other,Togglecheck, QQ监控发送
	WatchFolder(A_ScriptDir "\helper", "WatchConfDirFunc", False, 19)
Return

Toggle_Grayscale:
	WatchFolder(A_ScriptDir "\helper", "**DEL")
	IsGrayscale:=CaptureIni["SetGrayscale","IsGrayscale"]:=!IsGrayscale,CaptureIni.Save()
	Menu, other,Togglecheck, 灰度图
	WatchFolder(A_ScriptDir "\helper", "WatchConfDirFunc", False, 19)
	if IsGrayscale
		TrayTip,操作提示：,开启转化灰度图影响速度不适合连续上屏！,,2
Return

GetWindowClass:
	Highlightline:=new HighlightOutline()
	if (Hotkey="Ctrl"||Hotkey="LCtrl"||Hotkey="RCtrl")
		Hotkey,$%Hotkey%,Capturescreen,Off
	Loop
	{
		if GetKeyState("Ctrl","P")&&class{
			SendInput,{Ctrl UP}
			ToolTip
			Clipboard:=class
			TrayTip,Class获取提示：, 窗口类名已复制到剪切板！,,1
			Break
		}
		if GetKeyState("RButton"){
			ToolTip
			Break
		}
		MouseGetPos, x, y, Mid, MControl
		WinGetTitle, title, ahk_id %Mid%
		WinGetClass, class, ahk_id %Mid%
		WinGetPos , Xpos, Ypos, Width, Height, ahk_id%Mid%
		if (MControl<>""){
			ControlGetPos , X, Y, Width, Height, %MControl%, ahk_id%Mid%
			Xpos:=Xpos+x,Ypos:=Ypos+y
		}
		ToolTip, 窗口Class： %class%`nCtrl键复制窗口类名到剪切板！`n鼠标右键退出获取！
		Highlightline.show(Xpos, Ypos, Xpos+Width, Ypos+Height,2)
		Highlightline.setabove(Mid)
	}
	Highlightline.Destroy()
	Send,{Ctrl UP}
	if (Hotkey="Ctrl"||Hotkey="LCtrl"||Hotkey="RCtrl"){
		try
		{
			hotkey,$%hotkey%,Capturescreen,on
		}Catch e{
			TrayTip,触发热键Error,% e.Message?e.Message:e.Extra?e.Extra:"热键无法识别，请规范设置！",,3
		}
	}
Return

TrayMenuTip:
	Menu, TRAY,Tip, % (A_PtrSize>4?"候选框截图工具x64":"候选框截图工具x86") "`n - 触发热键：" Format_hotkey(Hotkey) "`n - 禁用／恢复：" Format_hotkey(SuspendedHotkey) "`n - 窗口类数量：" objlength(strsplit(Trim(WinClass,"|"),"|")) ("`n - 划选生成图片：" Format_hotkey(RenderHotkey))
Return

GetgrayLevel(Color){
	Color:=InStr(Color,"#")?SubStr(Format("0x{:06x}",SubStr(Color,2)),3):SubStr(Format("0x{:06x}",Color),3)
	tarr:=[Format("{:d}","0x" SubStr(Color,1,2)),Format("{:d}","0x" SubStr(Color,3,2)),Format("{:d}","0x" SubStr(Color,5,2))]

	Return Floor(tarr[1] * 0.299 + tarr[2] * 0.587 + tarr[3] * 0.114)
}

OpenClipboard(HWND:=0){
	DllCall("user32.dll\OpenClipboard","ptr",HWND)
}

EmptyClipboard(){
	Return DllCall("user32.dll\EmptyClipboard")
}

CloseClipboard(){
	Return DllCall("user32.dll\CloseClipboard")
}

EmptyMem(PID=""){
	pid:=!PID ? DllCall("GetCurrentProcessId") : pid
	h:=DllCall("OpenProcess", "UInt", 0x001F0FFF, "Int", 0, "Int", pid)
	DllCall("SetProcessWorkingSetSize", "UInt", h, "Int", -1, "Int", -1)
	DllCall("CloseHandle", "Int", h)
}

OnSuspended:
	Suspend,Toggle
	Menu, TRAY, Togglecheck, 禁用工具
	if A_IsSuspended {
		if !A_IsCompiled&&FileExist(A_ScriptDir "\Compiler\lock.ico"){
			Menu, Tray, Icon,%A_ScriptDir%\Compiler\lock.ico,,1
		}
		TrayTip,操作提示：,程序已禁用！,,2
	}Else{
		if A_IsCompiled
			Menu, TRAY,Icon,%A_ScriptFullPath%,1
		Else{
			if FileExist(A_ScriptDir "\Compiler\tools.ico")
				Menu, Tray, Icon,%A_ScriptDir%\Compiler\tools.ico,1
			Else
				Menu, Tray, Icon,shell32.dll,13
		}
		TrayTip,操作提示：,程序已启用！,,1
	}
Return

OnReload:
	Reload
Return

OnExits:
	ExitApp
Return

CheckCaptureIni:
	if !FileExist(A_ScriptDir "\helper")
		FileCreateDir,%A_ScriptDir%\helper
	For Section, element In default_obj
	{
		For key,value In element
		{
			if (CaptureIni[Section,key]="")
				%key%:=CaptureIni[Section,key]:=default_obj[Section,key]
			Else
				%key%:=CaptureIni[Section,key]
			if (CaptureIni.GetKeyComments(Section,key)=""&&DefaultComments[Section,key]<>"")
				CaptureIni.AddKeyComment(Section,key,DefaultComments[Section,key])
		}
	}
	CaptureIni.Save()

	Loop,Parse,WinClass,`|
	{
		if A_LoopField {
			ParseClass:=Trim(A_LoopField)
			GroupAdd, IMECLASS, ahk_class %ParseClass%
			GroupAdd, IMECLASS, ahk_class %ParseClass%*
		}
	}
	Loop,Parse,WatchWinClass,`|
	{
		if A_LoopField {
			ParseClass:=Trim(A_LoopField)
			GroupAdd, RenderClass, ahk_class %ParseClass%
			GroupAdd, RenderClass, ahk_class %ParseClass%*
		}
	}
	
	try
	{
		hotkey,$%hotkey%,Capturescreen,on
	}Catch e{
		TrayTip,触发热键Error,% e.Message?e.Message:e.Extra?e.Extra:"热键无法识别，请规范设置！",,3
	}
	try
	{
		hotkey,$%SuspendedHotkey%,OnSuspended,on
	}Catch e{
		TrayTip,禁用热键Error,% e.Message?e.Message:e.Extra?e.Extra:"热键无法识别，请规范设置！",,3
	}
	try
	{
		hotkey,$%RenderHotkey%,TextToImage,on
	}Catch e{
		TrayTip,文字转图片热键Error,% e.Message?e.Message:e.Extra?e.Extra:"热键无法识别，请规范设置！",,3
	}
	if IsGrayscale
			Menu, other,Check, 灰度图
	if IsWatchReturn
			Menu, other,Check, 监控发送

	grayLevel:=GetgrayLevel(StrLen(Subtitle.colorMap("red")?Subtitle.colorMap("red"):TextColor)=10?"0x" SubStr(Subtitle.colorMap("red")?Subtitle.colorMap("red"):TextColor,5):Subtitle.colorMap("red")?Subtitle.colorMap("red"):TextColor)>192?"deep":"light"
	Gosub TrayMenuTip
	WatchFolder(A_ScriptDir "\helper", "WatchConfDirFunc", False, 19)
Return

Format_hotkey(hk,flag=0){
	hk:=RegExReplace(hk,"\+","Shift+")
	hk:=RegExReplace(hk,"\^","Ctrl+")
	hk:=RegExReplace(hk,"\!","Alt+")
	hk:=RegExReplace(hk,"\#","Win+")
	hk:=RegExReplace(hk,"\<",flag?"":"L")
	hk:=RegExReplace(hk,"\>",flag?"":"R")
	hk:=RegExReplace(hk,"(\w)(\w+)*","$U1$2")

	Return hk
}

WatchConfDirFunc(Folder, Changes){
/*
	Change.Name
	Change.Action
	1—>"新增"
	2—>"移除"
	3—>"修改"
	4—>"重命名"
*/
	Global IniPath,CaptureIni
	For Each, Change In Changes
	{
		if Change.Action&&FileExist(Folder)&&!A_IsSuspended{
			if (Change.Action=3||Change.Action=2){
				WatchFolder(A_ScriptDir "\helper", "**DEL")
				if !FileExist(A_ScriptDir "\helper")
					FileCreateDir,%A_ScriptDir%\helper
				CaptureIni:=CaptureIni.Reload()
				Gosub CheckCaptureIni
			}
		}
	}
}

HBitmapToGrayscale(sBM){	; By SKAN on CR7J/D39L @ tiny.cc/tograyscale
	Local  ; Original ver. GDI_GrayscaleBitmap() @ https://autohotkey.com/board/topic/82794-

	P8:=(A_PtrSize=8),  VarSetCapacity(BM,P8? 32:24, 0)
	DllCall("GetObject", "Ptr",sBM, "Int",P8? 32:24, "Ptr",&BM)
	W := NumGet(BM,4,"Int"), H := NumGet(BM,8,"Int")
	sDC := DllCall( "CreateCompatibleDC", "Ptr",0, "Ptr")

	DllCall("DeleteObject", "Ptr",DllCall("SelectObject", "Ptr",sDC, "Ptr",sBM, "Ptr"))

	tBM := DllCall( "CopyImage", "Ptr"
		, DllCall( "CreateBitmap", "Int",1, "Int",1, "Int",0x1, "Int",8, "Ptr",0, "Ptr")
		, "Int",0, "Int",W, "Int",H, "Int",0x2008, "Ptr")

	tDC := DllCall("CreateCompatibleDC", "Ptr",0, "Ptr")
	DllCall("DeleteObject", "Ptr",DllCall("SelectObject", "Ptr",tDC, "Ptr",tBM, "Ptr"))

	Loop % (255, n:=0x000000, VarSetCapacity(RGBQUAD256,256*4,0))
		Numput(n+=0x010101, RGBQUAD256, A_Index*4, "Int")
	DllCall("SetDIBColorTable", "Ptr",tDC, "Int",0, "Int",256, "Ptr",&RGBQUAD256)

	DllCall("BitBlt",   "Ptr",tDC, "Int",0, "Int",0, "Int",W, "Int",H
		, "Ptr",sDC, "Int",0, "Int",0, "Int",0x00CC0020)

	Return % (tBM, DllCall("DeleteDC", "Ptr",sDC), DllCall("DeleteDC", "Ptr",tDC))
}

RunAsAdmin(){
	full_command_line := DllCall("GetCommandLine", "str")
	if not (A_IsAdmin or RegExMatch(full_command_line, " /restart(?!\S)")) {
		try {
			if A_IsCompiled
				Run *RunAs "%A_ScriptFullPath%" /restart
			else
				Run *RunAs "%A_AhkPath%" /restart "%A_ScriptFullPath%"
		}Catch e{
			MsgBox, 262160,Error,% e.Extra?e.Extra:"以管理员身份运行失败！",15
			ExitApp
		}
	}
}

; ==================================================================================================================================
; Function:       Notifies about changes within folders.
;                 This is a rewrite of HotKeyIt's WatchDirectory() released at
;                    http://www.autohotkey.com/board/topic/60125-ahk-lv2-watchdirectory-report-directory-changes/
; Tested with:    AHK 1.1.23.01 (A32/U32/U64)
; Tested on:      Win 10 Pro x64
; Usage:          WatchFolder(Folder, UserFunc[, SubTree := False[, Watch := 3]])
; Parameters:
;     Folder      -  The full qualified path of the folder to be watched.
;                    Pass the string "**PAUSE" and set UserFunc to either True or False to pause respectively resume watching.
;                    Pass the string "**END" and an arbitrary value in UserFunc to completely stop watching anytime.
;                    If not, it will be done internally on exit.
;     UserFunc    -  The name of a user-defined function to call on changes. The function must accept at least two parameters:
;                    1: The path of the affected folder. The final backslash is not included even if it is a drive's root
;                       directory (e.g. C:).
;                    2: An array of change notifications containing the following keys:
;                       Action:  One of the integer values specified as FILE_ACTION_... (see below).
;                                In case of renaming Action is set to FILE_ACTION_RENAMED (4).
;                       Name:    The full path of the changed file or folder.
;                       OldName: The previous path in case of renaming, otherwise not used.
;                       IsDir:   True if Name is a directory; otherwise False. In case of Action 2 (removed) IsDir is always False.
;                    Pass the string "**DEL" to remove the directory from the list of watched folders.
;     SubTree     -  Set to true if you want the whole subtree to be watched (i.e. the contents of all sub-folders).
;                    Default: False - sub-folders aren't watched.
;     Watch       -  The kind of changes to watch for. This can be one or any combination of the FILE_NOTIFY_CHANGES_...
;                    values specified below.
;                    Default: 0x03 - FILE_NOTIFY_CHANGE_FILE_NAME + FILE_NOTIFY_CHANGE_DIR_NAME
; Return values:
;     Returns True on success; otherwise False.
; Change history:
;     1.0.03.00/2021-10-14/just me        -  bug-fix for addding, removing, or updating folders.
;     1.0.02.00/2016-11-30/just me        -  bug-fix for closing handles with the '**END' option.
;     1.0.01.00/2016-03-14/just me        -  bug-fix for multiple folders
;     1.0.00.00/2015-06-21/just me        -  initial release
; License:
;     The Unlicense -> http://unlicense.org/
; Remarks:
;     Due to the limits of the API function WaitForMultipleObjects() you cannot watch more than MAXIMUM_WAIT_OBJECTS (64)
;     folders simultaneously.
; MSDN:
;     ReadDirectoryChangesW          msdn.microsoft.com/en-us/library/aa365465(v=vs.85).aspx
;     FILE_NOTIFY_CHANGE_FILE_NAME   = 1   (0x00000001) : Notify about renaming, creating, or deleting a file.
;     FILE_NOTIFY_CHANGE_DIR_NAME    = 2   (0x00000002) : Notify about creating or deleting a directory.
;     FILE_NOTIFY_CHANGE_ATTRIBUTES  = 4   (0x00000004) : Notify about attribute changes.
;     FILE_NOTIFY_CHANGE_SIZE        = 8   (0x00000008) : Notify about any file-size change.
;     FILE_NOTIFY_CHANGE_LAST_WRITE  = 16  (0x00000010) : Notify about any change to the last write-time of files.
;     FILE_NOTIFY_CHANGE_LAST_ACCESS = 32  (0x00000020) : Notify about any change to the last access time of files.
;     FILE_NOTIFY_CHANGE_CREATION    = 64  (0x00000040) : Notify about any change to the creation time of files.
;     FILE_NOTIFY_CHANGE_SECURITY    = 256 (0x00000100) : Notify about any security-descriptor change.
;     FILE_NOTIFY_INFORMATION        msdn.microsoft.com/en-us/library/aa364391(v=vs.85).aspx
;     FILE_ACTION_ADDED              = 1   (0x00000001) : The file was added to the directory.
;     FILE_ACTION_REMOVED            = 2   (0x00000002) : The file was removed from the directory.
;     FILE_ACTION_MODIFIED           = 3   (0x00000003) : The file was modified.
;     FILE_ACTION_RENAMED            = 4   (0x00000004) : The file was renamed (not defined by Microsoft).
;     FILE_ACTION_RENAMED_OLD_NAME   = 4   (0x00000004) : The file was renamed and this is the old name.
;     FILE_ACTION_RENAMED_NEW_NAME   = 5   (0x00000005) : The file was renamed and this is the new name.
;     GetOverlappedResult            msdn.microsoft.com/en-us/library/ms683209(v=vs.85).aspx
;     CreateFile                     msdn.microsoft.com/en-us/library/aa363858(v=vs.85).aspx
;     FILE_FLAG_BACKUP_SEMANTICS     = 0x02000000
;     FILE_FLAG_OVERLAPPED           = 0x40000000
; ==================================================================================================================================
WatchFolder(Folder, UserFunc, SubTree := False, Watch := 0x03) {
   Static DummyObject := {Base: {__Delete: Func("WatchFolder").Bind("**END", "")}}
   Static TimerID := "**" . A_TickCount
   Static TimerFunc := Func("WatchFolder").Bind(TimerID, "")
   Static MAXIMUM_WAIT_OBJECTS := 64
   Static MAX_DIR_PATH := 260 - 12 + 1
   Static SizeOfLongPath := MAX_DIR_PATH << !!A_IsUnicode
   Static SizeOfFNI := 0xFFFF ; size of the FILE_NOTIFY_INFORMATION structure buffer (64 KB)
   Static SizeOfOVL := 32     ; size of the OVERLAPPED structure (64-bit)
   Static WatchedFolders := {}
   Static EventArray := []
   Static WaitObjects := 0
   Static BytesRead := 0
   Static Paused := False
   ; ===============================================================================================================================
   If (Folder = "")
      Return False
   SetTimer, % TimerFunc, Off
   RebuildWaitObjects := False
   ; ===============================================================================================================================
   If (Folder = TimerID) { ; called by timer
      If (ObjCount := EventArray.Count()) && !Paused {
         ObjIndex := DllCall("WaitForMultipleObjects", "UInt", ObjCount, "Ptr", &WaitObjects, "Int", 0, "UInt", 0, "UInt")
         While (ObjIndex >= 0) && (ObjIndex < ObjCount) {
            Event := NumGet(WaitObjects, ObjIndex * A_PtrSize, "UPtr")
            Folder := EventArray[Event]
            If DllCall("GetOverlappedResult", "Ptr", Folder.Handle, "Ptr", Folder.OVLAddr, "UIntP", BytesRead, "Int", True) {
               Changes := []
               FNIAddr := Folder.FNIAddr
               FNIMax := FNIAddr + BytesRead
               OffSet := 0
               PrevIndex := 0
               PrevAction := 0
               PrevName := ""
               Loop {
                  FNIAddr += Offset
                  OffSet := NumGet(FNIAddr + 0, "UInt")
                  Action := NumGet(FNIAddr + 4, "UInt")
                  Length := NumGet(FNIAddr + 8, "UInt") // 2
                  Name   := Folder.Name . "\" . StrGet(FNIAddr + 12, Length, "UTF-16")
                  IsDir  := InStr(FileExist(Name), "D") ? 1 : 0
                  If (Name = PrevName) {
                     If (Action = PrevAction)
                        Continue
                     If (Action = 1) && (PrevAction = 2) {
                        PrevAction := Action
                        Changes.RemoveAt(PrevIndex--)
                        Continue
                     }
                  }
                  If (Action = 4)
                     PrevIndex := Changes.Push({Action: Action, OldName: Name, IsDir: 0})
                  Else If (Action = 5) && (PrevAction = 4) {
                     Changes[PrevIndex, "Name"] := Name
                     Changes[PrevIndex, "IsDir"] := IsDir
                  }
                  Else
                     PrevIndex := Changes.Push({Action: Action, Name: Name, IsDir: IsDir})
                  PrevAction := Action
                  PrevName := Name
               } Until (Offset = 0) || ((FNIAddr + Offset) > FNIMax)
               If (Changes.Length() > 0)
                  Folder.Func.Call(Folder.Name, Changes)
               DllCall("ResetEvent", "Ptr", Event)
               DllCall("ReadDirectoryChangesW", "Ptr", Folder.Handle, "Ptr", Folder.FNIAddr, "UInt", SizeOfFNI
                                              , "Int", Folder.SubTree, "UInt", Folder.Watch, "UInt", 0
                                              , "Ptr", Folder.OVLAddr, "Ptr", 0)
            }
            ObjIndex := DllCall("WaitForMultipleObjects", "UInt", ObjCount, "Ptr", &WaitObjects, "Int", 0, "UInt", 0, "UInt")
            Sleep, 0
         }
      }
   }
   ; ===============================================================================================================================
   Else If (Folder = "**PAUSE") { ; called to pause/resume watching
      Paused := !!UserFunc
      RebuildObjects := Paused
   }
   ; ===============================================================================================================================
   Else If (Folder = "**END") { ; called to stop watching
      For Event, Folder In EventArray {
         DllCall("CloseHandle", "Ptr", Folder.Handle)
         DllCall("CloseHandle", "Ptr", Event)
      }
      WatchedFolders := {}
      EventArray := []
      Paused := False
      Return True
   }
   ; ===============================================================================================================================
   Else { ; called to add, update, or remove folders
      Folder := RTrim(Folder, "\")
      VarSetCapacity(LongPath, MAX_DIR_PATH << !!A_IsUnicode, 0)
      If !DllCall("GetLongPathName", "Str", Folder, "Ptr", &LongPath, "UInt", MAX_DIR_PATH)
         Return False
      VarSetCapacity(LongPath, -1)
      Folder := LongPath
      If (WatchedFolders.HasKey(Folder)) { ; update or remove
         Event :=  WatchedFolders[Folder]
         FolderObj := EventArray[Event]
         DllCall("CloseHandle", "Ptr", FolderObj.Handle)
         DllCall("CloseHandle", "Ptr", Event)
         EventArray.Delete(Event)
         WatchedFolders.Delete(Folder)
         RebuildWaitObjects := True
      }
      If InStr(FileExist(Folder), "D") && (UserFunc <> "**DEL") && (EventArray.Count() < MAXIMUM_WAIT_OBJECTS) {
         If (IsFunc(UserFunc) && (UserFunc := Func(UserFunc)) && (UserFunc.MinParams >= 2)) && (Watch &= 0x017F) {
            Handle := DllCall("CreateFile", "Str", Folder . "\", "UInt", 0x01, "UInt", 0x07, "Ptr",0, "UInt", 0x03
                                          , "UInt", 0x42000000, "Ptr", 0, "UPtr")
            If (Handle > 0) {
               Event := DllCall("CreateEvent", "Ptr", 0, "Int", 1, "Int", 0, "Ptr", 0)
               FolderObj := {Name: Folder, Func: UserFunc, Handle: Handle, SubTree: !!SubTree, Watch: Watch}
               FolderObj.SetCapacity("FNIBuff", SizeOfFNI)
               FNIAddr := FolderObj.GetAddress("FNIBuff")
               DllCall("RtlZeroMemory", "Ptr", FNIAddr, "Ptr", SizeOfFNI)
               FolderObj["FNIAddr"] := FNIAddr
               FolderObj.SetCapacity("OVLBuff", SizeOfOVL)
               OVLAddr := FolderObj.GetAddress("OVLBuff")
               DllCall("RtlZeroMemory", "Ptr", OVLAddr, "Ptr", SizeOfOVL)
               NumPut(Event, OVLAddr + 8, A_PtrSize * 2, "Ptr")
               FolderObj["OVLAddr"] := OVLAddr
               DllCall("ReadDirectoryChangesW", "Ptr", Handle, "Ptr", FNIAddr, "UInt", SizeOfFNI, "Int", SubTree
                                              , "UInt", Watch, "UInt", 0, "Ptr", OVLAddr, "Ptr", 0)
               EventArray[Event] := FolderObj
               WatchedFolders[Folder] := Event
               RebuildWaitObjects := True
            }
         }
      }
      If (RebuildWaitObjects) {
         VarSetCapacity(WaitObjects, MAXIMUM_WAIT_OBJECTS * A_PtrSize, 0)
         OffSet := &WaitObjects
         For Event In EventArray
            Offset := NumPut(Event, Offset + 0, 0, "Ptr")
      }
   }
   ; ===============================================================================================================================
   If (EventArray.Count() > 0)
      SetTimer, % TimerFunc, -100
   Return (RebuildWaitObjects) ; returns True on success, otherwise False
}

Class HighlightOutline {
    __New(color="red") {
        static WS_EX_TRANSPARENT := 0x20, WS_EX_LAYERED := 0x00080000
        preDefaultGui := A_DefaultGui
        this.hwnds := {}
        Loop, 4 {
            Gui, New, -Caption +ToolWindow HWNDhwnd -DPIScale +E%WS_EX_TRANSPARENT% +E%WS_EX_LAYERED%
            Gui, Color, %color%
            this[A_Index] := hwnd
            DllCall("SetLayeredWindowAttributes", "ptr", hwnd, uint, 0, "uchar", 255, "int", 2)
        }
        this.isVisible := false
        this.color := color
        this.top := this[1]
        this.right := this[2]
        this.bottom := this[3]
        this.left := this[4]
        Gui, %preDefaultGui%: Default
    }
    MoveTogether(arrHwnd, arrPos) {
        hDWP := DllCall("BeginDeferWindowPos", "int", arrHwnd.Length(), "ptr")
        for i, v in arrPos {
            DllCall("DeferWindowPos", "ptr", hDWP, "ptr", arrHwnd[i], "ptr", 0
                , "int", v[1], "int", v[2], "int", v[3], "int", v[4]
                , "uint", 0x54) ; SWP_SHOWWINDOW|SWP_NOACTIVATE|SWP_NOZORDER
        }
        DllCall("EndDeferWindowPos", "ptr", hDWP)
    }
    Show(x1, y1, x2, y2,lineWidth=1) {
        b := lineWidth*(A_ScreenDPI/96)
        arrHwnd := [this.1, this.2, this.3, this.4]
        arrPos  := [ [x1-b, y1-b, x2-x1+b*2, b]
                   , [x2,   y1,   b,         y2-y1]
                   , [x1-b, y2,   x2-x1+b*2, b]
                   , [x1-b, y1,   b,         y2-y1] ]
        this.MoveTogether(arrHwnd, arrPos)
        ; this.GuiShow("top",    x1-b, y1-b, x2-x1+b*2, b)
        ; this.GuiShow("right",  x2,   y1,   b,         y2-y1)
        ; this.GuiShow("bottom", x1-b, y2,   x2-x1+b*2, b)
        ; this.GuiShow("left",   x1-b, y1,   b,         y2-y1)
        this.isVisible := true
    }
    GuiShow(name, x, y, w, h) {
        Gui, % this[name] ":Show", % "NA x" x " y" y " w" w " h" h
    }
    Hide() {
        Loop, 4
            Gui, % this[A_Index] ": Hide"
        this.isVisible := false
    }
    SetAbove(hwnd) {
        ABOVE := DllCall("GetWindow", "ptr", hwnd, "uint", 3, "ptr")
        Loop, 4
            DllCall(    "SetWindowPos", "ptr", this[A_Index], "ptr", ABOVE
                    ,    "int", 0, "int", 0, "int", 0, "int", 0
                    ,    "uint", 0x1|0x2|0x10    )
    }
    Transparent(param) {
        Loop, 4
            WinSet, Transparent, % param=1? 0:255, % "ahk_id" this[A_Index]
        this.isVisible := !param
    }
    Color(color) {
        Gui, +HWNDdefault
        Loop, 4
            Gui, % this[A_Index] ": Color" , %color%
        this.color := color
        Gui, %default%: Default
    }
    Destroy() {
        Loop, 4
            Gui, % this[A_Index] ": Destroy"
    }
}
